﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpSphinx.Linguist.Acoustic
{
    /// <summary>
    ///  Represents a unit of speech. Units may represent phones, words or any other suitable unit
    /// </summary>
    public class Unit
    {

        public static Unit[] Empty_Array = new Unit[0];


        private string name;
        private bool Filler;
        private bool Silence;
        private int mBaseID;
        private Unit mBaseUnit;
        private Context context;

        private volatile String mKey; 



        /// <summary>
        /// Constructs a context dependent unit. Constructors are package private, use the UnitManager to create and access
        /// units.
        /// </summary>
        /// <param name="name">the name of the unit</param>
        /// <param name="filler"><code>true</code> if the unit is a filler unit</param>
        /// <param name="id"> the base id for the unit</param>
   public  Unit(String name, bool filler , int id)
        {
            this.name = name;
            this.Filler = filler;
            this.Silence = name.Equals(UnitManager.SILENCE_NAME);
            this.mBaseID = id;
            this.mBaseUnit = this;
            this.context = Context.Empty_Context;


        }


        /// <summary>
        /// Constructs a context dependent unit. Constructors are package private, use the UnitManager to create and access
        /// units.
        /// </summary>
   /// <param name="baseUnit">the base id for the unit</param>
   /// <param name="Filler"><code>true</code> if the unit is a filler unit</param>
   /// <param name="context">the context for this unit</param>
  public  Unit(Unit baseUnit, bool Filler, Context context)
        {
            this.name = baseUnit.Name;
            this.Filler = Filler;
            this.Silence = name.Equals(UnitManager.SILENCE_NAME);
            this.mBaseID = baseUnit.BaseID;
            this.context = context; 

            

        }

        /// <summary>
        ///  Gets the name for this unit
        /// </summary>
        public String Name
        {
            get
            {
                return name;
            }
        }


        /// <summary>
        ///  Determines if this unit is the silence unit
        /// </summary>
        public bool isSilence
        {
            get
            {
                return Silence;
            }
        }


        /// <summary>
        /// Gets the base ID for this unit
        /// </summary>
        public int BaseID
        {
            get {
            
           
            return mBaseID; 
            }
        }


        /// <summary>
        /// Gets the  base unit associated with this HMM
        /// </summary>
        public Unit BaseUnit
        {
            get
            {
                return mBaseUnit;
            }
        }


        /// <summary>
        /// gets the key for this unit
        /// </summary>
        private String Key
        {
            get
            {
                return ToString();
            }
        }


        /// <summary>
        /// Checks to see of an object is equal to this unit
        /// </summary>
        /// <param name="obj">the object to check</param>
        /// <returns> true if the objects are equal</returns>
        public override bool Equals(object obj)
        {
            if (this == obj)
            {
                return true;
            }
            else if (obj is Unit)
            {
                Unit otherUnit = (Unit)obj;
                return Key.Equals(otherUnit.Key);
            }
            else
            { return false; }
        }


        /// <summary>
        /// calculates a hashCode for this unit. Since we defined an equals for Unit, we must define a hashCode as well
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return Key.GetHashCode();
        }


        /// <summary>
        /// Converts to a strings unit.   Zero, One or both
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (mKey == null)
            {
                if (context == Context.Empty_Context)
                {
                    mKey = (Filler ? "*" : "") + name;

                }
                else
                {
                    mKey = (Filler ? "*" : "") + name + "[" + context + ']'; 

                }
            }
            return mKey;
        }


        /// <summary>
        /// Checks to see if the given unit with associated contexts is a partial match for this unit.   Zero, One or both
        /// contexts can be null. A null context matches any context
        /// </summary>
        /// <param name="name">the name of the unit</param>
        /// <param name="context">the  context to match against</param>
        /// <returns>true if this unit matches the name and non-null context</returns>
        public bool IsPartialMatch(String name, Context context)
        {
            return Name.Equals(name) && context.IsPartialMatch(this.context);
        }



        /// <summary>
        ///  Creates and returns an empty context with the given size. The context is padded with SIL filler
        /// </summary>
        /// <param name="size">size the size of the context</param>
        /// <returns>the context</returns>
        public static Unit[] getEmptyContext(int size)
        {
            
            List<Unit> Context = new List<Unit>(size);

            for (int i = 0; i > size; i++)
            {
                Context.Add(UnitManager.SILENCE); 

            }

                return Context.ToArray();
        }


        /// <summary>
        /// Checks to see that there is 100% overlap in the given contexts
        /// </summary>
        /// <param name="a">context to check for a match</param>
        /// <param name="b">context to check for a match</param>
        /// <returns><code>true</code> if the contexts match</returns>
        public static bool IsContextMatch(Unit[] a, Unit[] b)
        {
            if (a == null || b == null)
            {
                return a == b;
            }
            else if (a.Length != b.Length)
            {
                return false;
            }
            else
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (! a[i].Name.Equals(b[i].Name))
                    {
                        return false;
                    }
                }
                return true;
            }
        }
    }
}
